package com.droid.library.widget.calendar

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.*
import android.os.Build
import android.support.annotation.RequiresApi
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.View
import com.droid.library.app.App
import com.xinye.lib.R
import java.util.*
import kotlin.collections.ArrayList

/**
 * 签到-日历View
 *
 * @author wangheng
 */
class CalendarView: View {

    companion object {
        private const val TAG = "CalendarView"
        private val DEBUG = App.getInstance().isDebug

        private val WEEK_LIST_DEFAULT = arrayOf("Sun","Mon","Tue","Wed","Thu","Fri","Sat")
        private val WEEK_BACKGROUND_COLOR_DEFAULT = Color.parseColor("#31A8F7")
        private const val WEEK_TEXT_COLOR_DEFAULT = Color.WHITE
        private const val WEEK_TEXT_SIZE_DEFAULT = 36f
        private const val WEEK_BACKGROUND_RADIUS = 12
        private const val WEEK_BACKGROUND_HEIGHT = 120f

        const val INVALID_GIFT_POSITION = -1

    }

    constructor(context: Context?) : super(context){
        initView(context,null)
    }
    constructor(context: Context?, attrs: AttributeSet?)
            : super(context, attrs){
        initView(context,attrs)
    }
    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int)
            : super(context, attrs, defStyleAttr){
        initView(context,attrs)
    }

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int)
            : super(context, attrs, defStyleAttr, defStyleRes){
        initView(context,attrs)
    }

    private fun initView(context: Context?, attrs: AttributeSet?) {
        if(context != null && attrs != null){

            val ta = context.obtainStyledAttributes(attrs, R.styleable.LoanCalendarView)
            // week
            mWeekBackgroundColor = ta.getColor(R.styleable.LoanCalendarView_weekBackgroundColor,mWeekBackgroundColor)
            mWeekTextColor = ta.getColor(R.styleable.LoanCalendarView_weekTextColor,mWeekTextColor)
            mWeekTextSize = ta.getDimensionPixelOffset(R.styleable.LoanCalendarView_weekTextSize,
                    mWeekTextSize.toInt()).toFloat()
            mWeekBackgroundHeight = ta.getDimensionPixelOffset(R.styleable.LoanCalendarView_weekBackgroundHeight,
                    mWeekBackgroundHeight.toInt()).toFloat()
            mWeekBackgroundRadius = ta.getDimensionPixelOffset(R.styleable.LoanCalendarView_weekBackgroundRadius,
                    mWeekBackgroundRadius)

            // point
            mPointColor = ta.getColor(R.styleable.LoanCalendarView_pointColor,mPointColor)
            mPointRadius = ta.getDimensionPixelOffset(R.styleable.LoanCalendarView_pointRadius,
                    mPointRadius.toInt()).toFloat()
            mPointToBottom = ta.getDimensionPixelOffset(R.styleable.LoanCalendarView_pointToBottom,
                    mPointToBottom.toInt()).toFloat()

            // day
            mPreviousTextColor = ta.getColor(R.styleable.LoanCalendarView_dayOfPreMonthTextColor,mPreviousTextColor)
            mPreviousTextSize = ta.getDimensionPixelOffset(R.styleable.LoanCalendarView_dayOfPreMonthTextSize,
                    mPreviousTextSize.toInt()).toFloat()

            mNextTextColor = ta.getColor(R.styleable.LoanCalendarView_dayOfNextMonthTextColor,mNextTextColor)
            mNextTextSize = ta.getDimensionPixelOffset(R.styleable.LoanCalendarView_dayOfNextMonthTextSize,
                    mNextTextSize.toInt()).toFloat()

            mCurrentTextColor = ta.getColor(R.styleable.LoanCalendarView_dayOfCurrentMonthTextColor,mCurrentTextColor)
            mCurrentTextSize = ta.getDimensionPixelOffset(R.styleable.LoanCalendarView_dayOfCurrentMonthTextSize,
                    mCurrentTextSize.toInt()).toFloat()

            mTodayTextColor = ta.getColor(R.styleable.LoanCalendarView_dayOfTodayTextColor,mTodayTextColor)
            mTodayTextSize = ta.getDimensionPixelOffset(R.styleable.LoanCalendarView_dayOfTodayTextSize,
                    mTodayTextSize.toInt()).toFloat()

            mDayHeight = ta.getDimensionPixelOffset(R.styleable.LoanCalendarView_dayHeight,mDayHeight)

            // divider
            mDividerOfWeekAndDay = ta.getDimensionPixelOffset(R.styleable.LoanCalendarView_weekAndDayDivider,
                    mDividerOfWeekAndDay)

            // gift
            val giftResourceId = ta.getResourceId(R.styleable.LoanCalendarView_dayGiftDrawable,0)
            if(giftResourceId > 0){
                mGiftBitmap = BitmapFactory.decodeResource(resources,giftResourceId)
            }
            ta.recycle()
        }

        initWeek()
        initDays()
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        mWeekBackgroundRect.set(0.toFloat(),0.toFloat(),width.toFloat(),mWeekBackgroundHeight)
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val height = mWeekBackgroundHeight + mDividerOfWeekAndDay + 6 * mDayHeight
        val heightSpecMode = MeasureSpec.EXACTLY
        val heightSpec = MeasureSpec.makeMeasureSpec(height.toInt(),heightSpecMode)
        super.onMeasure(widthMeasureSpec, heightSpec)
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        if(canvas != null){
            drawWeek(canvas)
            drawDays(canvas)
        }
    }
    // 星期日到星期六区域绘制
    private var mWeekList:ArrayList<String> = ArrayList(7)
    private var mWeekBackgroundColor = WEEK_BACKGROUND_COLOR_DEFAULT
    private var mWeekTextColor = WEEK_TEXT_COLOR_DEFAULT
    private var mWeekTextSize = WEEK_TEXT_SIZE_DEFAULT
    private var mWeekBackgroundRadius = WEEK_BACKGROUND_RADIUS
    private var mWeekBackgroundRect = RectF(0f,0f,0f,0f)
    private var mWeekBackgroundHeight = WEEK_BACKGROUND_HEIGHT
    private var mWeekBackgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG)
    private var mWeekTextPaint = Paint(Paint.ANTI_ALIAS_FLAG)

    private fun initWeek() {
        mWeekList.clear()
        mWeekList.addAll(WEEK_LIST_DEFAULT)

        mWeekBackgroundPaint.color = mWeekBackgroundColor
        mWeekBackgroundPaint.style = Paint.Style.FILL_AND_STROKE

        mWeekTextPaint.color = mWeekTextColor
        mWeekTextPaint.style = Paint.Style.FILL_AND_STROKE
        mWeekTextPaint.textSize = mWeekTextSize

    }

    private fun drawWeek(canvas: Canvas){
        drawWeekBackground(canvas)
        drawWeekText(canvas)
    }

    private fun drawWeekBackground(canvas: Canvas) {
        val radius = mWeekBackgroundRadius.toFloat()
        canvas.drawRoundRect(mWeekBackgroundRect, radius,radius,mWeekBackgroundPaint)
    }

    private fun drawWeekText(canvas: Canvas) {
        val drawPerWidth = width / 7f
        var drawStart = 0f

        val fontMetrics = mWeekTextPaint.fontMetricsInt
        val baseline = (mWeekBackgroundRect.bottom + mWeekBackgroundRect.top - fontMetrics.bottom - fontMetrics.top) / 2

        for(index in 0 until mWeekList.size){
            val text = mWeekList[index]
            val start = drawStart + (drawPerWidth - mWeekTextPaint.measureText(text)) / 2
            canvas.drawText(text,start,baseline,mWeekTextPaint)

            drawStart += drawPerWidth
        }
    }

    // 日期绘制
    class Day(val year:Int, val month:Int, val day: Int, val type: Int){
        companion object {
            const val TYPE_PREVIOUS_MONTH_DAY = 1
            const val TYPE_NEXT_MONTH_DAY = 2
            const val TYPE_CURRENT_MONTH_DAY = 3
            const val TYPE_TODAY = 4
        }

        var isGiftDay: Boolean = false
        var isSignDay: Boolean = false

        override fun toString(): String {
            return "$year-${month + 1}-$day,type:$type"
        }
    }

    private val mPreviousMonthSelectDay = ArrayList<Int>()
    private val mCurrentMonthSelectDay = ArrayList<Int>()
    private var mGiftDayDelta = -1
    /**
     * 设置选择日
     *
     * @param previousMonth 上个月选中的日期
     * @param currentMonth 当前月选中的日期
     * @param deltaGiftDay 礼物Icon距离今天的位置偏移量 >= 0
     */
    fun setSelectDay(previousMonth: ArrayList<Int>,currentMonth: ArrayList<Int>,deltaGiftDay: Int){
        mPreviousMonthSelectDay.clear()
        mPreviousMonthSelectDay.addAll(previousMonth)

        mCurrentMonthSelectDay.clear()
        mCurrentMonthSelectDay.addAll(currentMonth)

        mGiftDayDelta = deltaGiftDay
    }

    private val mDayList = ArrayList<Day>(42)

    fun setYearMonthDay(year: Int,month:Int,day: Int){

        mDayList.clear()
        addDayOfPreviousMonthToList(year,month)

        // 添加当月日期
        var giftDayIndex = -1
        val lastDay = getLastDayOfMonth(year,month)
        for(current in 1..lastDay){
            if(current == day) {
                val currentDay = Day(year,month,current, Day.TYPE_TODAY)
                currentDay.isSignDay = mCurrentMonthSelectDay.contains(current)
                mDayList.add(currentDay)
                if(mGiftDayDelta < 0){
                    giftDayIndex = INVALID_GIFT_POSITION
                }else{
                    giftDayIndex = mDayList.size - 1 + mGiftDayDelta
                }
                if(DEBUG) {
                    logger("current month day(today):$currentDay")
                }
            }else{
                val currentDay = Day(year,month,current, Day.TYPE_CURRENT_MONTH_DAY)
                currentDay.isSignDay = mCurrentMonthSelectDay.contains(current)
                mDayList.add(currentDay)
                logger("current month day:$currentDay")
            }

        }

        // 添加下个月日期
        val countDayOfNextMonth = 42 - mDayList.size
        val nextYear = if(month == 11){year + 1}else{year}
        val nextMonth = if(month == 11){0}else{month + 1}
        for(current in 1.. countDayOfNextMonth){
            val currentDay = Day(nextYear,nextMonth,current, Day.TYPE_NEXT_MONTH_DAY)
            mDayList.add(currentDay)
            logger("next month day:$currentDay")
        }

        // 设置礼物日期
        if(giftDayIndex >= 0 && giftDayIndex < mDayList.size){
            mDayList[giftDayIndex].isGiftDay = true
        }

        invalidate()

    }

    // 添加上个月日期(今天是周几减去周日的天数就是上个月的天数)
    private fun addDayOfPreviousMonthToList(year: Int,month: Int) {
        val calendar = Calendar.getInstance()
        calendar.set(Calendar.YEAR,year)
        calendar.set(Calendar.MONTH,month)
        calendar.set(Calendar.DAY_OF_MONTH,1)

        val countOfPreviousMonth = calendar.get(Calendar.DAY_OF_WEEK) - Calendar.SUNDAY
        val yearOfPreviousMonth = if (month == 0) {
            year - 1
        } else {
            year
        }
        val previousMonth = if (month == 0) {
            11
        } else {
            month - 1
        }
        val lastDayOfPreviousMonth = getLastDayOfMonth(yearOfPreviousMonth, previousMonth)
        val first = lastDayOfPreviousMonth - countOfPreviousMonth + 1
        val last = lastDayOfPreviousMonth

        for (current in first..last) {
            val day = Day(yearOfPreviousMonth,previousMonth,current,Day.TYPE_PREVIOUS_MONTH_DAY)
            day.isSignDay = mPreviousMonthSelectDay.contains(current)
            mDayList.add(day)
            logger("previous month day:$day")
        }
    }

    /**
     * 得到指定年、月(0-11)的最后一天
     */
    private fun getLastDayOfMonth(year: Int, month:Int): Int{
        val calendar = Calendar.getInstance()
        calendar.set(Calendar.YEAR,year)
        calendar.set(Calendar.MONTH,month)
        calendar.set(calendar.get(Calendar.YEAR), month + 1, 1)
        calendar.add(Calendar.DATE, -1)
        return calendar.get(Calendar.DAY_OF_MONTH)
    }

    private var mPreviousTextColor = Color.parseColor("#CACACA")
    private var mNextTextColor = Color.parseColor("#CACACA")
    private var mCurrentTextColor = Color.parseColor("#4A4A4A")
    private var mTodayTextColor = Color.parseColor("#31A8F7")
    private var mPointColor = Color.parseColor("#31A8F7")

    private var mPreviousTextSize = 48f
    private var mNextTextSize = 48f
    private var mCurrentTextSize = 48f
    private var mTodayTextSize = 48f
    private var mPointRadius = dip2px(2f).toFloat()
    private var mPointToBottom = dip2px(5f).toFloat()

    private var mPreviousPaint = Paint(Paint.ANTI_ALIAS_FLAG)
    private var mNextPaint = Paint(Paint.ANTI_ALIAS_FLAG)
    private var mCurrentPaint = Paint(Paint.ANTI_ALIAS_FLAG)
    private var mTodayPaint = Paint(Paint.ANTI_ALIAS_FLAG)
    private var mPointPaint = Paint(Paint.ANTI_ALIAS_FLAG)

    private var mDividerOfWeekAndDay = dip2px(8f)
    private var mDayHeight = dip2px(40f)
    private var mGiftBitmap: Bitmap? = null

    private fun initDays(){
        mPreviousPaint.color = mPreviousTextColor
        mPreviousPaint.textSize = mPreviousTextSize

        mNextPaint.color = mNextTextColor
        mNextPaint.textSize = mNextTextSize

        mCurrentPaint.color = mCurrentTextColor
        mCurrentPaint.textSize = mCurrentTextSize

        mTodayPaint.color = mTodayTextColor
        mTodayPaint.textSize = mTodayTextSize

        mPointPaint.color = mPointColor

        mGiftBitmap = BitmapFactory.decodeResource(resources, R.drawable.icon_star_gray)
    }

    private fun drawDays(canvas: Canvas) {
        val list = mDayList

        val perWidth = width / 7f               // 每一个日期的宽度
        var drawLeft = 0f
        var drawTop = mWeekBackgroundHeight + mDividerOfWeekAndDay
        var drawBottom = drawTop + mDayHeight

        val countOfDayList = list.size
        for(index in 0 until countOfDayList){
            if(index % 7 == 0){
                drawLeft = 0f
                if(index != 0) {
                    drawTop += mDayHeight
                    drawBottom = drawTop + mDayHeight
                }
            }
            // 绘制文字
            val day = list[index]
            when(day.type){
                Day.TYPE_PREVIOUS_MONTH_DAY -> {
                    drawSingleDay(canvas,day.day.toString(),drawLeft,drawTop,drawBottom,perWidth,mPreviousPaint)
                }
                Day.TYPE_NEXT_MONTH_DAY -> {
                    drawSingleDay(canvas,day.day.toString(),drawLeft,drawTop,drawBottom,perWidth,mNextPaint)
                }
                Day.TYPE_CURRENT_MONTH_DAY -> {
                    drawSingleDay(canvas,day.day.toString(),drawLeft,drawTop,drawBottom,perWidth,mCurrentPaint)
                }
                Day.TYPE_TODAY -> {
                    drawSingleDay(canvas,day.day.toString(),drawLeft,drawTop,drawBottom,perWidth,mTodayPaint)
                }
            }
            // 绘制小圆点,如果需要绘制的礼物，则不绘制小圆点
            if(day.isSignDay && !day.isGiftDay) {
                val centerX = drawLeft + perWidth / 2
                val centerY = drawBottom - (mPointToBottom + mPointRadius)
                canvas.drawCircle(centerX, centerY, mPointRadius, mPointPaint)
            }

            // 绘制礼物
            if(day.isGiftDay){
                val bitmap = mGiftBitmap
                if(bitmap != null) {
                    val left = drawLeft + (perWidth - bitmap.width) / 2f
                    val top = drawTop + (mDayHeight - bitmap.height) / 2f
                    canvas.drawBitmap(bitmap,left,top,null)
                }
            }

            drawLeft += perWidth
        }
    }

    private fun drawSingleDay(canvas: Canvas,text: String,drawLeft: Float,drawTop: Float,drawBottom: Float,drawWidth: Float,paint: Paint){
        val fontMetrics = paint.fontMetricsInt
        val baseline = (drawBottom + drawTop - fontMetrics.bottom - fontMetrics.top) / 2

        val start = drawLeft + (drawWidth - paint.measureText(text)) / 2
        canvas.drawText(text,start,baseline,paint)
    }

    fun dip2px(dpValue: Float): Int {
        val scale = context.resources.displayMetrics.density
        return (dpValue * scale + 0.5f).toInt()
    }

    fun px2dip(pxValue: Float): Int {
        val scale = context.resources.displayMetrics.density
        return (pxValue / scale + 0.5f).toInt()
    }

    private fun sp2px(spValue: Float): Int {
        val fontScale = context.resources.displayMetrics.scaledDensity
        return (spValue * fontScale + 0.5f).toInt()
    }

    private fun logger(message: String){
        if(DEBUG){
            Log.i(TAG,message)
        }
    }

    interface OnDayClickListener {
        fun onDayClick(day: Day)
    }

    private var mOnDayClickListener: OnDayClickListener? = null

    fun setOnDayClickListener(listener: OnDayClickListener?){
        mOnDayClickListener = listener
    }

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent?): Boolean {
        val x = event?.x ?: 0f
        val y = event?.y ?: 0f
        when(event?.action){
            MotionEvent.ACTION_DOWN -> {
                if(x > 0 && x < width &&
                        y > mWeekBackgroundHeight + mDividerOfWeekAndDay && y < height){
                    return true
                }
            }
            MotionEvent.ACTION_UP -> {
                val upX = event.x.toInt()
                val upY = event.y.toInt()
                if(x > 0 && x < width &&
                        y > mWeekBackgroundHeight + mDividerOfWeekAndDay && y < height){

                    val realHeight = (upY -  mWeekBackgroundHeight - mDividerOfWeekAndDay).toInt()

                    val perWidth = (width / 7f).toInt()
                    val perHeight = mDayHeight
                    val indexX = upX / perWidth
                    val indexY = realHeight / perHeight
                    val index = indexY * 7 + indexX

                    if(index < mDayList.size && index >= 0){
                        mOnDayClickListener?.onDayClick(mDayList[index])
                        logger("indexX:$indexX,indexY:$indexY,index:$index ,click day:${mDayList[index]}")
                    }

                    return true
                }
            }
        }
        return super.onTouchEvent(event)
    }

}